#include <ncurses.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/select.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>

#include "server/game.h"
#include "update/system.h"
#include "update/proc.h"
#include "client/inet.h"
#include "client/game.h"

extern uint8_t q_map[WIDTH_AREA][HEIGHT_AREA];
extern int serv_sock;

#define NANOSEC 1000000
#define COEFF (100*NANOSEC)

//TODO:
//Решить проблему с синхронизацией?
//Улучшить генерацию мапы(похуй)
int main(int argc, char *argv[])
{
    struct sockaddr_in serv_addr;
    struct timespec fr_d;
    struct timespec time_zero;
    struct timespec wait_start, wait_end;

    int8_t ret;

    int direction;
    int prev_direction;

    uint32_t frame_delay;
    uint32_t num_players;

    char arg_frame_delay[SIZE_ARG] = "300";
    char arg_num_players[SIZE_ARG] = "4"; char arg_port[SIZE_ARG] = "2229";
    char arg_addr[SIZE_ARG] = "127.0.0.1";
    char arg_log_file[SIZE_ARG] = {0};
    uint8_t arg_mod = 'c';

    char name[MAX_NAME_LEN+1];

    ex_player_t *cur_player;

    fd_set server_fd;
    fd_set read_fd;
    
    while ((ret = getopt(argc, argv, "hsf:d:n:p:a:")) != -1)
    {
        switch (ret)
        {
            case 'd':
                strncpy(arg_frame_delay, optarg, SIZE_ARG);
            break;

            case 'h':
                printf("%s", HELP_CLIENT);
                printf("\n");
                printf("%s", HELP_SERVER);
                exit(EXIT_SUCCESS);
            break;

            case 'n':
                strncpy(arg_num_players, optarg, SIZE_ARG);
            break;

            case 'p':
                strncpy(arg_port, optarg, SIZE_ARG);
            break;

            case 'a':
                strncpy(arg_addr, optarg, SIZE_ARG);
            break;

            case 's':
                arg_mod = 's';
            break; 

            case 'f':
                strncpy(arg_log_file, optarg, SIZE_ARG);
            break;
        }
    }
    
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(atoi(arg_port));
    serv_addr.sin_addr.s_addr = Inet_addr(arg_addr);
    
    if (arg_mod == 's')
    {
        pid_t pid;
        char *arg_exe[14] = {0};

        pid = Fork();
        if (pid == 0)
        {
            arg_exe[0] = "./server/server.exe";
            arg_exe[1] = "-p";
            arg_exe[2] = arg_port;
            arg_exe[3] = "-d";
            arg_exe[4] = arg_frame_delay;
            arg_exe[5] = "-n";
            arg_exe[6] = arg_num_players;
            
            if (strlen(arg_log_file) != 0)
            {
                arg_exe[7] = "-f";
                arg_exe[8] = arg_log_file;
            }
            else
            {
                arg_exe[7] = "-f";
                arg_exe[8] = "server.log";
            }

            execv("./server/server.exe", arg_exe);
        }
    }

    printf("ENTER NAME: ");
    fgets(name, MAX_NAME_LEN, stdin);

    name[strlen(name)-1] = '\0';

    connect_to_server(&serv_addr, name);
    get_map((uint8_t *)q_map);
    expand_map();

    initscr();
    noecho();
    keypad(stdscr, true);
    curs_set(0);
    nodelay(stdscr, true);
    cbreak();
    start_color();
    init_pair(PLAYER_PAIR, COLOR_RED, COLOR_BLACK);
    init_pair(ENEMY_PAIR, COLOR_BLUE, COLOR_BLACK);

    ready();
    cur_player = start_game(name, &frame_delay, &num_players);
    counting_food(num_players);

    FD_ZERO(&server_fd);
    FD_ZERO(&read_fd);

    FD_SET(serv_sock, &server_fd);

    prev_direction = -1;
    time_zero.tv_nsec = 0;
    time_zero.tv_sec = 0;

    draw_map();
    while (true)
    {
        clock_gettime(CLOCK_MONOTONIC, &wait_start);
        read_fd = server_fd;

        print_players(cur_player);
        print_score(3, 3, cur_player);
        refresh();

        if ((direction = getch()) != ERR)
        {
            if (direction == KEY_UP)
                direction = UP;
            else if (direction == KEY_DOWN)
                direction = DOWN;
            else if (direction == KEY_RIGHT)
                direction = RIGHT;
            else if (direction == KEY_LEFT)
                direction = LEFT;
            else
                continue;

            if (prev_direction == direction)
                goto NEXT_ITER;

            send_state(name, direction);
            prev_direction = direction;
        }

    NEXT_ITER:
        clock_gettime(CLOCK_MONOTONIC, &wait_end);

        fr_d.tv_sec = 0;
        fr_d.tv_nsec = frame_delay*NANOSEC - (wait_end.tv_nsec - wait_start.tv_nsec) + COEFF; 
        nanosleep(&fr_d, NULL);

        Pselect(serv_sock+1, &read_fd, &time_zero);

        if (FD_ISSET(serv_sock, &read_fd))
        {
            while (1)
                if (!get_state())
                    break;
        }

        move_players(name, direction);

        if (check_end_game())
            end_game();
    }
}

